/**
 * Copyright (c) 2010 sonntagsfarben.de
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
 
package de.sonntagsfarben.logger.output
{
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.XMLSocket;

	public class SOSLogOutput implements ILogOutput
	{
		private var _socket:XMLSocket;
		private var _queue:Array;
		private var _clearOnConnect:Boolean;


		public function SOSLogOutput(clearOnConnect:Boolean = true, hostname:String = "localhost", port:int = 4445)
		{
			_clearOnConnect = clearOnConnect;
			_queue = [];

			_socket = new XMLSocket;
			_socket.addEventListener(Event.CONNECT, onConnect);
			_socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
			_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			_socket.connect(hostname, port);
		}


		public function log(logLevel:String, classDefinition:Object = null, methodName:String = "", log:* = null):void
		{
			var message:String = String(log);

			if (message.length == 0)
				return;

			if (_socket.connected)
			{
				_sendToSocket(logLevel, classDefinition, methodName, message);
			}
			else
			{
				if (_queue)
					_queue.push(new LogObject(logLevel, classDefinition, methodName, message));
			}
		}


		/**
		 * Resets the output of SOS.
		 */
		public function clear():void
		{
			_socket.send("<clear/>\n");
		}


		/**
		 * Disconnects from the socket.
		 */
		public function destroy():void
		{
			_socket.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			_socket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			_socket.removeEventListener(Event.CONNECT, onConnect);

			try
			{
				_socket.close();
			}
			catch (error:Error)
			{
				trace(error);
			}
		}


		/**
		 * Sends a message to the socket.
		 *
		 * @param logLevel Log level
		 * @param message Message
		 */
		private function _sendToSocket(logLevel:String, classDefinition:Object = null, methodName:String = "", log:* = null):void
		{
			var xmlMessage:String = "<showMessage key=\"" + logLevel + "\"/> " + classDefinition + " " + methodName + " " + log;
			_socket.send(xmlMessage + "\n");
		}


		/**
		 * Handler for Socket connect.
		 * Sends all messages in the queue.
		 *
		 * @param event
		 */
		private function onConnect(event:Event):void
		{
			if (_clearOnConnect)
				clear();

			for each (var logObject:LogObject in _queue)
			{
				_sendToSocket(logObject.logLevel, logObject.classDefinition, logObject.methodName, logObject.message);
			}

			_queue = null;
		}


		/**
		 * Handler for Socker errors.
		 * Destroys the publisher.
		 */
		private function onError(event:Event):void
		{
			_queue = null;

			destroy();
		}
	}
}

internal class LogObject
{
	public var logLevel:String;
	public var message:String;
	public var classDefinition:Object;
	public var methodName:String;


	public function LogObject(logLevel:String, classDefinition:Object = null, methodName:String = "", log:* = null)
	{
		this.methodName = methodName;
		this.classDefinition = classDefinition;
		this.logLevel = logLevel;
		this.message = String(log);
	}
}
